jetcrab\bytecode\statements/
switch_statement.rs

1use crate::ast::Node;
2use crate::vm::instructions::Instruction;
3use crate::vm::types::CodeAddress;
4
5use super::ControlFlowCore;
6
7pub fn generate_switch_statement<T>(this: &mut T, node: &Node)
8where
9    T: ControlFlowCore,
10{
11    if let Node::SwitchStatement(stmt) = node {
12        // Generate discriminant expression
13        this.visit_node(&stmt.discriminant);
14
15        // Store the discriminant value for comparison
16        this.instructions().push(Instruction::Dup);
17
18        // Generate all case tests and bodies
19        let mut case_jumps = Vec::new();
20        let mut case_bodies = Vec::new();
21        let mut case_tests = Vec::new();
22
23        for case in &stmt.cases {
24            if let Some(test) = &case.test {
25                // Generate test expression
26                this.visit_node(test);
27
28                // Compare with discriminant
29                this.instructions().push(Instruction::Eq);
30
31                // Jump to case body if equal
32                let jump_pos = this.instructions().len();
33                this.instructions()
34                    .push(Instruction::JumpIfTrue(CodeAddress::new(0))); // Placeholder
35                case_jumps.push(jump_pos);
36                case_tests.push(true);
37            } else {
38                // Default case - no test needed
39                case_jumps.push(0);
40                case_tests.push(false);
41            }
42
43            // Mark case body start
44            let body_start = this.instructions().len();
45            case_bodies.push(body_start);
46
47            // Generate case body
48            for cons in &case.consequent {
49                this.visit_node(cons);
50            }
51
52            // Jump to end of switch (to avoid fall-through)
53            let _jump_to_end_pos = this.instructions().len();
54            this.instructions()
55                .push(Instruction::Jump(CodeAddress::new(0))); // Placeholder
56
57            // Update case jump address
58            if let Some(jump_pos) = case_jumps.last_mut() {
59                if *jump_pos > 0 {
60                    this.instructions()[*jump_pos] =
61                        Instruction::JumpIfTrue(CodeAddress::new(body_start));
62                }
63            }
64        }
65
66        // Mark end of switch
67        let switch_end = this.instructions().len();
68
69        // Update all jump-to-end addresses
70        for i in case_bodies {
71            // Find the jump instruction after each case body
72            if i < this.instructions().len() - 1 {
73                if let Instruction::Jump(addr) = &this.instructions()[i + 1] {
74                    if addr.as_usize() == 0 {
75                        this.instructions()[i + 1] =
76                            Instruction::Jump(CodeAddress::new(switch_end));
77                    }
78                }
79            }
80        }
81
82        // Pop the duplicated discriminant
83        this.instructions().push(Instruction::Pop);
84    }
85}